import copy

from util import Logger as log
import json, os


class Block:
    def __init__(self, x: int, y: int, order: int, texture: tuple[str, str] | str, type: str):
        self.x = x
        self.y = y
        self.order = order
        self.texture = texture
        self.type = type

    def __copy__(self):
        return Block(self.x,self.y,self.order,self.texture,self.type)


class Player(Block):
    move, direction = False, 1

    def __copy__(self):
        player = Player(self.x,self.y,self.order,self.texture,self.type)
        player.move = self.move
        player.direction = self.direction
        return player


class Level:
    def __init__(self, blocks: dict[int, list[Block]], name: str, player: Player, add_player_to_blocks=True):
        self.blocks = blocks
        self.name = name
        self.player = player
        if add_player_to_blocks:
            blocks[player.order].append(player)

    def __copy__(self):
        new_blocks = {}
        for key in self.blocks:
               new_blocks[key] = []
               for block in self.blocks[key]:
                   if not isinstance(block,Player):
                       new_blocks[key].append(block.__copy__())
        new_player = self.player.__copy__()
        return Level(new_blocks, self.name, new_player)


levels: dict[str, Level] = {}


def read_levels():
    global levels
    base_dir = './levels'
    files = [os.path.join(base_dir, file) for file in os.listdir(base_dir)]
    for file in files:
        if file.endswith(".json"):
            log.info(f"read levels -> {file}")

            with open(file, "r", encoding="UTF-8") as f:
                dic = json.load(f)

            background = dic["background"]
            blocks = dic["blocks"]
            order = dic["order"]

            new_blocks = {0: [], background["order"]: []}
            for i in range(20):
                for j in range(20):
                    if not background["texture"] == "none":
                        block = Block(i, j, background["order"], background["texture"].split(":"), "none")
                    else:
                        block = Block(i, j, background["order"], "none", "none")
                    new_blocks[background["order"]].append(block)

            for b in blocks:
                if not b["texture"] == "none":
                    block = Block(b["x"], b["y"], b.get("order", order), b["texture"].split(":"), b["type"])
                else:
                    block = Block(b["x"], b["y"], b.get("order", order), "none", b["type"])

                if b.get("order", order) in new_blocks and isinstance(new_blocks[b.get("order", order)], list):
                    new_blocks[b.get("order", order)].append(block)
                else:
                    new_blocks[b.get("order", order)] = []
                    new_blocks[b.get("order", order)].append(block)
            levels[os.path.splitext(os.path.basename(file))[0]] = Level(new_blocks,
                                                                        os.path.splitext(os.path.basename(file))[0],
                                                                        Player(dic["player"]["x"], dic["player"]["y"],
                                                                               0, ("player", "player_walk_1"),
                                                                               "player"))


def get_level(name: str) -> Level:
    return levels[name].__copy__()


def init():
    log.info("LevelManager -> init")
    log.info("LevelManager -> read levels")

    read_levels()


def is_block(level: Level, x: int, y: int, block_type: str) -> Block | None:
    for key in sorted(level.blocks.keys()):
        blocks: list[Block] = level.blocks[key]
        for block in blocks:
            if block.x == x and block.y == y and block.type == block_type:
                return block

    return None

def is_collide(level: Level, x: int, y: int) -> bool:
    for key in sorted(level.blocks.keys()):
        blocks: list[Block] = level.blocks[key]
        for block in blocks:
            if block.x == x and block.y == y and (block.type == "wall" or block.type == "box"):
                return False

    return True


def is_wall(level: Level, x: int, y: int) -> Block | None:
    return is_block(level, x, y, "wall")


def is_box(level: Level, x: int, y: int) -> Block | None:
    return is_block(level, x, y, "box")


def move_block(level: Level, block: Block, new_x: int, new_y: int):
    block.x = new_x
    block.y = new_y
    sort_blocks(level)


def move_player(level: Level, down: int, right: int):
    move = False
    if not right == 0 and 0 <= level.player.x + right <= 19:
        level.player.x += right
        move = True
    if not down == 0 and 0 <= level.player.y + down <= 19:
        level.player.y += down
        move = True

    d = 0
    # 1 ↓👇 2 →👉 3 ↑👆 4 ←👈
    if down > 0:
        d = 1
    elif down < 0:
        d = 3
    if right > 0:
        d = 2
    elif right < 0:
        d = 4

    level.player.move = move
    if not d == 0:
        level.player.direction = d
    if move:
        sort_blocks(level)


def sort_blocks(level: Level):
    for key in level.blocks.keys():
        level.blocks[key].sort(key=lambda b: b.y)
